﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JF.RealtimeNotifications.Notifiers.Extensibility;
using Ibt.Ortc.Api.Extensibility;
using Ibt.Ortc.Api;
using System.Threading.Tasks;
using System.Configuration;

namespace JF.RealtimeNotifications.Notifiers.Realtime
{
    public class RealtimeNotifier : ISubscriberNotifier, IPublisherNotifier
    {
        public String ApplicationKey
        {
            get
            {
                return ConfigurationManager.AppSettings["RealtimeApplicationKey"];
            }
        }
        public String AuthenticationToken
        {
            get
            {
                return ConfigurationManager.AppSettings["RealtimeAuthenticationToken"];
            }
        }
        public String Url
        {
            get
            {
                return ConfigurationManager.AppSettings["RealtimeUrl"];
            }
        }
        public String PluginsDirectory { get; set; }

        private OrtcClient client;
        private Dictionary<String, OnMessageDelegate> subscriptionsBuffer;
        private List<Notification> notificationBuffer;

        public RealtimeNotifier()
        {
            PluginsDirectory = "Plugins";

            subscriptionsBuffer = new Dictionary<string, OnMessageDelegate>();
            notificationBuffer = new List<Notification>();
        }

        public void Connect(OnConnectDelegate callback)
        {
            if (client == null)
            {
                var ortc = new Ortc(PluginsDirectory);
                var ortcFactory = ortc.LoadOrtcFactory("IbtRealTimeSJ");

                client = ortcFactory.CreateClient();
                client.ConnectionMetadata = "Realtime Notifier";

                client.OnException += (sender, error) =>
                {
                    throw error;
                };
            }

            client.OnConnected += (sender) =>
            {
                this.EmptySubscriptionsBuffer();
                this.EmptyNotificationsBuffer();

                if (callback != null)
                {
                    callback(null);
                }
            };

            client.OnReconnected += (sender) =>
            {
                this.EmptySubscriptionsBuffer();
                this.EmptyNotificationsBuffer();
            };

            client.ClusterUrl = Url;
            client.Connect(ApplicationKey, AuthenticationToken);
        }

        public void Subscribe(Notification notification, OnNotificationDelegate onNotification)
        {
            if (client == null)
            {
                throw new RealtimeNotifierConnectionException("Realtime client is null");
            }

            if (!client.IsConnected)
            {
                subscriptionsBuffer.Add(notification.Title, (sender, channel, message) =>
                {
                    var notificationReceived = new RealtimeNotification(channel, message);

                    onNotification(notificationReceived);
                });
            }
            else
            {
                client.Subscribe(notification.Title, true, (sender, channel, message) =>
                {
                    var notificationReceived = new RealtimeNotification(channel, message);

                    onNotification(notificationReceived);
                });
            }
        }

        public void Publish(Notification notification)
        {
            if (client == null)
            {
                throw new RealtimeNotifierConnectionException("Realtime client is null");
            }

            if (!client.IsConnected)
            {
                notificationBuffer.Add(notification);
            }
            else
            {
                client.Send(notification.Title, notification.Text);
            }
        }

        private void EmptySubscriptionsBuffer()
        {
            Task subscriptionsTask = new Task(() =>
            {
                lock (subscriptionsBuffer)
                {
                    foreach (var subscription in subscriptionsBuffer)
                    {
                        client.Subscribe(subscription.Key, true, subscription.Value);
                    }
                    subscriptionsBuffer.Clear();
                }
            });


            subscriptionsTask.Start();
        }

        private void EmptyNotificationsBuffer()
        {
            Task notificationsTask = new Task(() =>
            {
                lock (notificationBuffer)
                {
                    foreach (var notification in notificationBuffer)
                    {
                        client.Send(notification.Title, notification.Text);
                    }
                    notificationBuffer.Clear();
                }
            });

            notificationsTask.Start();
        }
    }
}
